function _ctParseLocalDate(s) { s = (s || '').trim(); if (!s) return new Date(); // Accept ISO-ish like 2026-05-14 09:00 or 2026-05-14T09:00 var m = s.match(/^(\d{4})-(\d{2})-(\d{2})[ T](\d{1,2}):(\d{2})(?::(\d{2}))?$/); if (m) { return new Date(Date.UTC(+m[1], +m[2]-1, +m[3], +m[4], +m[5], +(m[6]||0))); } var m2 = s.match(/^(\d{4})-(\d{2})-(\d{2})$/); if (m2) { return new Date(Date.UTC(+m2[1], +m2[2]-1, +m2[3])); } var d = new Date(s); if (!isNaN(d.getTime())) return d; return null; } function _ctZoneOffsetMinutes(date, tz) { try { var dtf = new Intl.DateTimeFormat('en-US', { timeZone: tz, year:'numeric', month:'2-digit', day:'2-digit', hour:'2-digit', minute:'2-digit', second:'2-digit', hour12:false }); var parts = {}; dtf.formatToParts(date).forEach(function(p){ if (p.type !== 'literal') parts[p.type] = p.value; }); var asUTC = Date.UTC(+parts.year, +parts.month-1, +parts.day, +parts.hour === 24 ? 0 : +parts.hour, +parts.minute, +parts.second); return (asUTC - date.getTime()) / 60000; } catch (e) { return 0; } } function convert(input) { var fromTz = (document.getElementById('tz-from') || {}).value; var toTz = (document.getElementById('tz-to') || {}).value; if (!fromTz || !toTz) return ''; var localInFrom = _ctParseLocalDate(input); if (!localInFrom) return 'Could not parse the date / time. Try something like 2026-05-14 09:00'; // localInFrom is currently parsed as a UTC instant with the wall-clock components. // Subtract the source zone offset to find the real UTC instant. var fromOffset = _ctZoneOffsetMinutes(localInFrom, fromTz); var trueUtc = new Date(localInFrom.getTime() - fromOffset * 60000); var optsLong = { timeZone: toTz, dateStyle: 'full', timeStyle: 'long' }; var optsShort = { timeZone: toTz, year:'numeric', month:'2-digit', day:'2-digit', hour:'2-digit', minute:'2-digit', hour12:false }; var pretty = new Intl.DateTimeFormat(undefined, optsLong).format(trueUtc); var iso = new Intl.DateTimeFormat('sv-SE', optsShort).format(trueUtc); var utcStr = trueUtc.toISOString().replace('T', ' ').replace('.000Z', ' UTC'); return pretty + '\n' + iso + ' (' + toTz + ')' + '\n\nUTC instant: ' + utcStr; } function _ctPopulateZones() { var zones = []; try { if (Intl.supportedValuesOf) zones = Intl.supportedValuesOf('timeZone'); } catch (e) {} if (!zones.length) zones = ['UTC','America/Los_Angeles','America/Denver','America/Chicago','America/New_York','Europe/London','Europe/Paris','Europe/Berlin','Asia/Dubai','Asia/Singapore','Asia/Tokyo','Australia/Sydney','Pacific/Auckland']; var fromSel = document.getElementById('tz-from'); var toSel = document.getElementById('tz-to'); if (!fromSel || !toSel) return; var local = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'; zones.forEach(function(z) { fromSel.appendChild(new Option(z, z)); toSel.appendChild(new Option(z, z)); }); fromSel.value = local; toSel.value = (local === 'UTC' ? 'America/New_York' : 'UTC'); } $(function(){ _ctPopulateZones(); $('#tz-from, #tz-to').on('change', function(){ $('#box1').trigger('keyup'); }); $('#tz-swap').on('click', function(){ var f = $('#tz-from').val(); var t = $('#tz-to').val(); $('#tz-from').val(t); $('#tz-to').val(f); $('#box1').trigger('keyup'); }); // Default to current local time if input empty if (!$('#box1').val()) { var now = new Date(); var pad = function(n){ return n < 10 ? '0' + n : '' + n; }; $('#box1').val(now.getFullYear() + '-' + pad(now.getMonth()+1) + '-' + pad(now.getDate()) + ' ' + pad(now.getHours()) + ':' + pad(now.getMinutes())).trigger('keyup'); } }); var _loadedScripts = {}; function loadScriptPromise(url) { if (_loadedScripts[url]) return _loadedScripts[url]; _loadedScripts[url] = new Promise(function (resolve, reject) { var s = document.createElement('script'); s.src = url; s.onload = resolve; s.onerror = reject; document.head.appendChild(s); }); return _loadedScripts[url]; } function replaceAll(find, replace, str) { return str.replace(new RegExp(find, 'g'), replace); } function beautify(str) { var result = ''; var length = str.length; var i = 0; var braceCountLeft = 0; var braceCountRight = 0; var withinQuotes = false; while (i < length) { var c = str[i]; if (c == '"' && (i == 0 || c[i - 1] != '\\')) { // non-escaped quotes withinQuotes = !withinQuotes; } if (!withinQuotes && (c == '}' || c == '{' || c == ',')) { console.log('Start####' + result); // look back and remove carriage returns and whitespace that are already there var resultIndex = result.length - 1; while (resultIndex >= 0 && (result[resultIndex] == ' ' || result[resultIndex] == '\r' || result[resultIndex] == '\n' || result[resultIndex] == '\t')) { resultIndex = resultIndex - 1; result = result.substr(0, resultIndex + 1); console.log('char ' + result[resultIndex] + '-----' + result + 'zzz ' + result.length + ' ' + resultIndex); } if (c == '{') { braceCountLeft++; result += c + '\r' + GetTabs(braceCountLeft - braceCountRight); } else if (c == '}') { braceCountRight++; // precede with carriage return result += '\r' + GetTabs(braceCountLeft - braceCountRight) + c; } else if (c == ',') { result += c + '\r' + GetTabs(braceCountLeft - braceCountRight); } var nextChar = ''; // advance through whitespace and remove carriage returns that are already there while (i < length && (str[i + 1] == ' ' || str[i + 1] == '\r' || str[i + 1] == '\n' || str[i + 1] == '\t')) { i++; } } else { result += str[i]; } i++; } return result; } function GetTabs(count) { var result = ''; for (var i = 0; i < count; i++) { result += ' '; } return result; }